// 版权所有2009年围棋作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package typecheck

import (
	"fmt"
	"sort"
	"strconv"
	"strings"

	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/types"
	"cmd/internal/src"
)

func AssignConv(n ir.Node, t *types.Type, context string) ir.Node {
	return assignconvfn(n, t, func() string { return context })
}

// DotImportRefs将导入的标识映射回
// ir.PkgName他们是通过dot导入的。
var DotImportRefs map[*ir.Ident]*ir.PkgName

// LookupNum查找以前缀开头，以结尾的符号
// 小数点。若前缀太长，LookupNum将陷入恐慌。
func LookupNum(prefix string, n int) *types.Sym {
	var buf [20]byte // 足够长的时间供所有当前用户使用
	copy(buf[:], prefix)
	b := strconv.AppendInt(buf[:len(prefix)], int64(n), 10)
	return types.LocalPkg.LookupBytes(b)
}

// 给定funarg结构列表，返回fn参数列表。
func NewFuncParams(tl *types.Type, mustname bool) []*ir.Field {
	var args []*ir.Field
	gen := 0
	for _, t := range tl.Fields().Slice() {
		s := t.Sym
		if mustname && (s == nil || s.Name == "_") {
			// 发明一个名字，这样我们就可以在蹦床上看到它
			s = LookupNum(".anon", gen)
			gen++
		} else if s != nil && s.Pkg != types.LocalPkg {
			// TODO（mdempsky）：保留原始位置、名称和包。
			s = Lookup(s.Name)
		}
		a := ir.NewField(base.Pos, s, nil, t.Type)
		a.Pos = t.Pos
		a.IsDDD = t.IsDDD()
		args = append(args, a)
	}

	return args
}

// newname返回与符号s关联的新ONAME节点。
func NewName(s *types.Sym) *ir.Name {
	n := ir.NewNameAt(base.Pos, s)
	n.Curfn = ir.CurFunc
	return n
}

// NodAddr返回一个表示base.Pos处的&n的节点。
func NodAddr(n ir.Node) *ir.AddrExpr {
	return NodAddrAt(base.Pos, n)
}

// nodAddrPos返回一个表示位置位置处的&n的节点。
func NodAddrAt(pos src.XPos, n ir.Node) *ir.AddrExpr {
	n = markAddrOf(n)
	return ir.NewAddrExpr(pos, n)
}

func markAddrOf(n ir.Node) ir.Node {
	if IncrementalAddrtaken {
		// 我们只能在确定的情况下进行增量addrtaken计算
		// 键入OADDR的参数。只有在事故发生后才安全
		// 主类型检查已完成。
		// OADDR的参数需要进行类型检查，因为&x[i]需要
		// 如果x是数组，则x的地址，如果x是片，则不是。
		// 注意：只有在选中n之后，OuterValue才能正常工作。
		n = typecheck(n, ctxExpr)
		if x := ir.OuterValue(n); x.Op() == ir.ONAME {
			x.Name().SetAddrtaken(true)
		}
	} else {
		// 请记住，我们构建OADDR时没有计算
		// 它的论点。稍后我们将使用ComputeAddressTake批量执行此操作。
		DirtyAddrtaken = true
	}
	return n
}

// 如果IncrementAladdrTake为false，则不计算OADDR节点的Addrtaken
// 当它建成时。由ComputeAddressTake批量设置Addrtaken位。
// 如果IncrementAladdrTake为true，则在构建OADDR节点时，Addrtaken
// 其参数的字段将立即更新。
var IncrementalAddrtaken = false

// 如果DirtyAddrtaken为true，则存在其相应参数的OADDR
// 尚未标记为Addrtaken。
var DirtyAddrtaken = false

func ComputeAddrtaken(top []ir.Node) {
	for _, n := range top {
		var doVisit func(n ir.Node)
		doVisit = func(n ir.Node) {
			if n.Op() == ir.OADDR {
				if x := ir.OuterValue(n.(*ir.AddrExpr).X); x.Op() == ir.ONAME {
					x.Name().SetAddrtaken(true)
					if x.Name().IsClosureVar() {
						// 将原始变量标记为Addrtaken，以便capturevars
						// 知道不通过价值传递它。
						x.Name().Defn.Name().SetAddrtaken(true)
					}
				}
			}
			if n.Op() == ir.OCLOSURE {
				ir.VisitList(n.(*ir.ClosureExpr).Func.Body, doVisit)
			}
		}
		ir.Visit(n, doVisit)
	}
}

func NodNil() ir.Node {
	n := ir.NewNilExpr(base.Pos)
	n.SetType(types.Types[types.TNIL])
	return n
}

// AddImplicitDots在obj.field中查找缺少的字段
// 将提供最短的唯一地址和
// 修改缺少字段名的树。
func AddImplicitDots(n *ir.SelectorExpr) *ir.SelectorExpr {
	n.X = typecheck(n.X, ctxType|ctxExpr)
	if n.X.Diag() {
		n.SetDiag(true)
	}
	t := n.X.Type()
	if t == nil {
		return n
	}

	if n.X.Op() == ir.OTYPE {
		return n
	}

	s := n.Sel
	if s == nil {
		return n
	}

	switch path, ambig := dotpath(s, t, nil, false); {
	case path != nil:
		// 重建省略点
		for c := len(path) - 1; c >= 0; c-- {
			dot := ir.NewSelectorExpr(base.Pos, ir.ODOT, n.X, path[c].field.Sym)
			dot.SetImplicit(true)
			dot.SetType(path[c].field.Type)
			n.X = dot
		}
	case ambig:
		base.Errorf("ambiguous selector %v", n)
		n.X = nil
	}

	return n
}

func CalcMethods(t *types.Type) {
	if t == nil || t.AllMethods().Len() != 0 {
		return
	}

	// 标记顶级方法符号
	// 因此，ExpD1不考虑它们。
	for _, f := range t.Methods().Slice() {
		f.Sym.SetUniq(true)
	}

	// 生成所有可到达的方法
	slist = slist[:0]
	expand1(t, true)

	// 检查每个方法是否唯一可访问
	var ms []*types.Field
	for i, sl := range slist {
		slist[i].field = nil
		sl.field.Sym.SetUniq(false)

		var f *types.Field
		path, _ := dotpath(sl.field.Sym, t, &f, false)
		if path == nil {
			continue
		}

		// dotpath可能已经挖出了任意字段，我们只需要方法。
		if !f.IsMethod() {
			continue
		}

		// 将其添加到基类型方法列表中
		f = f.Copy()
		f.Embedded = 1 // 我需要一个蹦床
		for _, d := range path {
			if d.field.Type.IsPtr() {
				f.Embedded = 2
				break
			}
		}
		ms = append(ms, f)
	}

	for _, f := range t.Methods().Slice() {
		f.Sym.SetUniq(false)
	}

	ms = append(ms, t.Methods().Slice()...)
	sort.Sort(types.MethodsByName(ms))
	t.SetAllMethods(ms)
}

// adddot1返回类型t中深度d处名为s的字段或方法的数量。
// 如果正好存在一个，它将在*save中返回（如果save不是nil），
// 点列表将包含为找到它而遍历的嵌入字段的路径，
// 按相反的顺序。如果不存在，则“更多”将指示t是否包含任何
// 在深度d处嵌入字段，以便调用者可以决定是否在
// 更大的深度。
func adddot1(s *types.Sym, t *types.Type, d int, save **types.Field, ignorecase bool) (c int, more bool) {
	if t.Recur() {
		return
	}
	t.SetRecur(true)
	defer t.SetRecur(false)

	var u *types.Type
	d--
	if d < 0 {
		// 我们已经到达目标深度。如果t有任何字段/方法
		// 叫s，我们就完了。否则，我们还需要检查
		// 下面是嵌入式字段。
		c = lookdot0(s, t, save, ignorecase)
		if c != 0 {
			return c, false
		}
	}

	u = t
	if u.IsPtr() {
		u = u.Elem()
	}
	if !u.IsStruct() && !u.IsInterface() {
		return c, false
	}

	var fields *types.Fields
	if u.IsStruct() {
		fields = u.Fields()
	} else {
		fields = u.AllMethods()
	}
	for _, f := range fields.Slice() {
		if f.Embedded == 0 || f.Sym == nil {
			continue
		}
		if d < 0 {
			// 在目标深度找到一个嵌入字段。
			return c, true
		}
		a, more1 := adddot1(s, f.Type, d, save, ignorecase)
		if a != 0 && c == 0 {
			dotlist[d].field = f
		}
		c += a
		if more1 {
			more = true
		}
	}

	return c, more
}

// adddot1使用点列表记录嵌入字段的路径
// 用于访问目标字段或方法。
// 必须为非nil，以便即使d为零，dotpath也返回非nil切片。
var dotlist = make([]dlist, 10)

// 将分配的节点n转换为类型t。
func assignconvfn(n ir.Node, t *types.Type, context func() string) ir.Node {
	if n == nil || n.Type() == nil || n.Type().Broke() {
		return n
	}

	if t.Kind() == types.TBLANK && n.Type().Kind() == types.TNIL {
		base.Errorf("use of untyped nil")
	}

	n = convlit1(n, t, false, context)
	if n.Type() == nil {
		return n
	}
	if t.Kind() == types.TBLANK {
		return n
	}

	// 将理想布尔值从比较转换为普通布尔值
	// 如果下一步是非bool（如接口{}）。
	if n.Type() == types.UntypedBool && !t.IsBoolean() {
		if n.Op() == ir.ONAME || n.Op() == ir.OLITERAL {
			r := ir.NewConvExpr(base.Pos, ir.OCONVNOP, nil, n)
			r.SetType(types.Types[types.TBOOL])
			r.SetTypecheck(1)
			r.SetImplicit(true)
			n = r
		}
	}

	if types.Identical(n.Type(), t) {
		return n
	}

	op, why := Assignop(n.Type(), t)
	if op == ir.OXXX {
		base.Errorf("cannot use %L as type %v in %s%s", n, t, context(), why)
		op = ir.OCONV
	}

	r := ir.NewConvExpr(base.Pos, op, t, n)
	r.SetTypecheck(1)
	r.SetImplicit(true)
	return r
}

// src类型分配是否与dst类型兼容？
// 如果是，则返回要在转换中使用的操作码。
// 如果没有，返回OXXX。在这种情况下，字符串返回参数可能
// 持有一个理由。在所有其他情况下，它将是空字符串。
func Assignop(src, dst *types.Type) (ir.Op, string) {
	if src == dst {
		return ir.OCONVNOP, ""
	}
	if src == nil || dst == nil || src.Kind() == types.TFORW || dst.Kind() == types.TFORW || src.Underlying() == nil || dst.Underlying() == nil {
		return ir.OXXX, ""
	}

	// 1.src类型与dst相同。
	if types.Identical(src, dst) {
		return ir.OCONVNOP, ""
	}

	// 2.src和dst具有相同的底层类型
	// 并且src或dst不是命名类型或
	// 两者都是空接口类型。
	// 对于可分配但不同的非空接口类型，
	// 我们想重新计算itab。重新计算itab可确保
	// ITAB是唯一的（因此是具有编译时的接口）
	// 类型I具有接口类型I）的itab。
	if types.Identical(src.Underlying(), dst.Underlying()) {
		if src.IsEmptyInterface() {
			// 两个空接口之间的转换
			// 不需要代码。
			return ir.OCONVNOP, ""
		}
		if (src.Sym() == nil || dst.Sym() == nil) && !src.IsInterface() {
			// 两种类型之间的转换，至少一种未命名，
			// 不需要转换。例外情况是非空接口
			// 需要更新itab。
			return ir.OCONVNOP, ""
		}
	}

	// 3.dst是一种接口类型，src实现dst。
	if dst.IsInterface() && src.Kind() != types.TNIL {
		var missing, have *types.Field
		var ptr int
		if implements(src, dst, &missing, &have, &ptr) {
			// 调用NeedITab/ITabAddr以便（src、dst）
			// 提前添加到itabs，这允许
			// 让我们通过这个系统去虚拟化通话
			// 稍后输入/接口对。请参见reflect.go中的CompileITabs
			if types.IsDirectIface(src) && !dst.IsEmptyInterface() {
				NeedITab(src, dst)
			}

			return ir.OCONVIFACE, ""
		}

		// 无论如何，我们都会抱怨这种方法，抑制虚假消息。
		if have != nil && have.Sym == missing.Sym && (have.Type.Broke() || missing.Type.Broke()) {
			return ir.OCONVIFACE, ""
		}

		var why string
		if isptrto(src, types.TINTER) {
			why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", src)
		} else if have != nil && have.Sym == missing.Sym && have.Nointerface() {
			why = fmt.Sprintf(":\n\t%v does not implement %v (%v method is marked 'nointerface')", src, dst, missing.Sym)
		} else if have != nil && have.Sym == missing.Sym {
			why = fmt.Sprintf(":\n\t%v does not implement %v (wrong type for %v method)\n"+
				"\t\thave %v%S\n\t\twant %v%S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
		} else if ptr != 0 {
			why = fmt.Sprintf(":\n\t%v does not implement %v (%v method has pointer receiver)", src, dst, missing.Sym)
		} else if have != nil {
			why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)\n"+
				"\t\thave %v%S\n\t\twant %v%S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
		} else {
			why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)", src, dst, missing.Sym)
		}

		return ir.OXXX, why
	}

	if isptrto(dst, types.TINTER) {
		why := fmt.Sprintf(":\n\t%v is pointer to interface, not interface", dst)
		return ir.OXXX, why
	}

	if src.IsInterface() && dst.Kind() != types.TBLANK {
		var missing, have *types.Field
		var ptr int
		var why string
		if implements(dst, src, &missing, &have, &ptr) {
			why = ": need type assertion"
		}
		return ir.OXXX, why
	}

	// 4.src为双向信道值，dst为信道类型，
	// src和dst具有相同的元素类型，并且
	// dst类型或未命名为src。
	if src.IsChan() && src.ChanDir() == types.Cboth && dst.IsChan() {
		if types.Identical(src.Elem(), dst.Elem()) && (src.Sym() == nil || dst.Sym() == nil) {
			return ir.OCONVNOP, ""
		}
	}

	// 5.src是预先声明的标识符nil，dst是可为零的类型。
	if src.Kind() == types.TNIL {
		switch dst.Kind() {
		case types.TPTR,
			types.TFUNC,
			types.TMAP,
			types.TCHAN,
			types.TINTER,
			types.TSLICE:
			return ir.OCONVNOP, ""
		}
	}

	// 6.关于非类型化常量的规则-已由DefaultLit转换。

	// 7.任何类型的值都可以分配给空白标识符。
	if dst.Kind() == types.TBLANK {
		return ir.OCONVNOP, ""
	}

	return ir.OXXX, ""
}

// 我们可以将src类型的值转换为dst类型的值吗？
// 如果是，则返回要在转换中使用的操作代码（可能是OCONVNOP）。
// 如果没有，返回OXXX。在这种情况下，字符串返回参数可能
// 持有一个理由。在所有其他情况下，它将是空字符串。
// srccontent表示src类型的值是否为常量。
func Convertop(srcConstant bool, src, dst *types.Type) (ir.Op, string) {
	if src == dst {
		return ir.OCONVNOP, ""
	}
	if src == nil || dst == nil {
		return ir.OXXX, ""
	}

	// 不允许从常规转换为go:notinheap
	// （除非它不安全。指针）。这些是特定于运行时的
	// 规则。
	// （a） 不允许（*T）到（*U）T所在的位置：不在范围内，但U不在范围内。
	if src.IsPtr() && dst.IsPtr() && dst.Elem().NotInHeap() && !src.Elem().NotInHeap() {
		why := fmt.Sprintf(":\n\t%v is incomplete (or unallocatable), but %v is not", dst.Elem(), src.Elem())
		return ir.OXXX, why
	}
	// （b） 不允许字符串到[]T，其中T为go:notinheap。
	if src.IsString() && dst.IsSlice() && dst.Elem().NotInHeap() && (dst.Elem().Kind() == types.ByteType.Kind() || dst.Elem().Kind() == types.RuneType.Kind()) {
		why := fmt.Sprintf(":\n\t%v is incomplete (or unallocatable)", dst.Elem())
		return ir.OXXX, why
	}

	// 1.src可以分配给dst。
	op, why := Assignop(src, dst)
	if op != ir.OXXX {
		return op, why
	}

	// 接口的规则在转换中没有什么不同
	// 而不是作业。如果涉及接口，请立即停止
	// 有来自assignop的好消息。
	// 否则清除错误。
	if src.IsInterface() || dst.IsInterface() {
		return ir.OXXX, why
	}

	// 2.忽略结构标记，src和dst具有相同的底层类型。
	if types.IdenticalIgnoreTags(src.Underlying(), dst.Underlying()) {
		return ir.OCONVNOP, ""
	}

	// 3.src和dst是未命名的指针类型，忽略结构标记，
	// 它们的基类型具有相同的基础类型。
	if src.IsPtr() && dst.IsPtr() && src.Sym() == nil && dst.Sym() == nil {
		if types.IdenticalIgnoreTags(src.Elem().Underlying(), dst.Elem().Underlying()) {
			return ir.OCONVNOP, ""
		}
	}

	// 4.src和dst都是整数或浮点类型。
	if (src.IsInteger() || src.IsFloat()) && (dst.IsInteger() || dst.IsFloat()) {
		if types.SimType[src.Kind()] == types.SimType[dst.Kind()] {
			return ir.OCONVNOP, ""
		}
		return ir.OCONV, ""
	}

	// 5.src和dst都是复杂类型。
	if src.IsComplex() && dst.IsComplex() {
		if types.SimType[src.Kind()] == types.SimType[dst.Kind()] {
			return ir.OCONVNOP, ""
		}
		return ir.OCONV, ""
	}

	// 常量转换的特殊情况：任何数值
	// 转换可能是好的。我们将进一步验证
	// 在evconst内。见#38117。
	if srcConstant && (src.IsInteger() || src.IsFloat() || src.IsComplex()) && (dst.IsInteger() || dst.IsFloat() || dst.IsComplex()) {
		return ir.OCONV, ""
	}

	// 6.src是一个整数或具有类型[]字节或[]符文
	// dst是一种字符串类型。
	if src.IsInteger() && dst.IsString() {
		return ir.ORUNESTR, ""
	}

	if src.IsSlice() && dst.IsString() {
		if src.Elem().Kind() == types.ByteType.Kind() {
			return ir.OBYTES2STR, ""
		}
		if src.Elem().Kind() == types.RuneType.Kind() {
			return ir.ORUNES2STR, ""
		}
	}

	// 7.src是字符串，dst是[]字节或[]符文。
	// 要切片的字符串。
	if src.IsString() && dst.IsSlice() {
		if dst.Elem().Kind() == types.ByteType.Kind() {
			return ir.OSTR2BYTES, ""
		}
		if dst.Elem().Kind() == types.RuneType.Kind() {
			return ir.OSTR2RUNES, ""
		}
	}

	// 8.src是指针或uintptr，dst是不安全的。指针。
	if (src.IsPtr() || src.IsUintptr()) && dst.IsUnsafePtr() {
		return ir.OCONVNOP, ""
	}

	// 9src不安全。指针和dst是指针或uintptr。
	if src.IsUnsafePtr() && (dst.IsPtr() || dst.IsUintptr()) {
		return ir.OCONVNOP, ""
	}

	// 10src是map，dst是指向相应hmap的指针。
	// 此规则对于以下实现细节是必需的：
	// go gc映射被实现为指向hmap结构的指针。
	if src.Kind() == types.TMAP && dst.IsPtr() &&
		src.MapType().Hmap == dst.Elem() {
		return ir.OCONVNOP, ""
	}

	// 11src是一个片，dst是指向数组的指针。
	// 它们必须具有相同的元素类型。
	if src.IsSlice() && dst.IsPtr() && dst.Elem().IsArray() &&
		types.Identical(src.Elem(), dst.Elem().Elem()) {
		if !types.AllowsGoVersion(curpkg(), 1, 17) {
			return ir.OXXX, ":\n\tconversion of slices to array pointers only supported as of -lang=go1.17"
		}
		return ir.OSLICE2ARRPTR, ""
	}

	return ir.OXXX, ""
}

// 用于解析嵌入类型中的省略点的代码。

// 数据列表存储一个指针，指向嵌入其中的TFIELD类型
// 一种染色剂或染色剂类型。
type dlist struct {
	field *types.Field
}

// 点路径计算唯一的最短显式选择器路径以完全限定
// 选择表达式x.f，其中x为t型，f为符号s。
// 如果不存在这样的路径，dotpath将返回nil。
// 如果同一深度有多条最短路径，则ambig为真。
func dotpath(s *types.Sym, t *types.Type, save **types.Field, ignorecase bool) (path []dlist, ambig bool) {
	// 在结构中嵌入类型将树结构强加于
	// 类型：结构是它们嵌入的类型的父对象，类型是它们的父对象
	// 字段或方法。我们在这里的目标是找到到达目的地的最短路径
	// 在以t为根的子树中名为s的字段或方法。完成
	// 也就是说，我们迭代地执行深度优先搜索，不断增加深度
	// 直到找到指定的字段/方法或耗尽树。
	for d := 0; ; d++ {
		if d > len(dotlist) {
			dotlist = append(dotlist, dlist{})
		}
		if c, more := adddot1(s, t, d, save, ignorecase); c == 1 {
			return dotlist[:d], false
		} else if c > 1 {
			return nil, true
		} else if !more {
			return nil, false
		}
	}
}

func expand0(t *types.Type) {
	u := t
	if u.IsPtr() {
		u = u.Elem()
	}

	if u.IsInterface() {
		for _, f := range u.AllMethods().Slice() {
			if f.Sym.Uniq() {
				continue
			}
			f.Sym.SetUniq(true)
			slist = append(slist, symlink{field: f})
		}

		return
	}

	u = types.ReceiverBaseType(t)
	if u != nil {
		for _, f := range u.Methods().Slice() {
			if f.Sym.Uniq() {
				continue
			}
			f.Sym.SetUniq(true)
			slist = append(slist, symlink{field: f})
		}
	}
}

func expand1(t *types.Type, top bool) {
	if t.Recur() {
		return
	}
	t.SetRecur(true)

	if !top {
		expand0(t)
	}

	u := t
	if u.IsPtr() {
		u = u.Elem()
	}

	if u.IsStruct() || u.IsInterface() {
		var fields *types.Fields
		if u.IsStruct() {
			fields = u.Fields()
		} else {
			fields = u.AllMethods()
		}
		for _, f := range fields.Slice() {
			if f.Embedded == 0 {
				continue
			}
			if f.Sym == nil {
				continue
			}
			expand1(f.Type, false)
		}
	}

	t.SetRecur(false)
}

func ifacelookdot(s *types.Sym, t *types.Type, ignorecase bool) (m *types.Field, followptr bool) {
	if t == nil {
		return nil, false
	}

	path, ambig := dotpath(s, t, &m, ignorecase)
	if path == nil {
		if ambig {
			base.Errorf("%v.%v is ambiguous", t, s)
		}
		return nil, false
	}

	for _, d := range path {
		if d.field.Type.IsPtr() {
			followptr = true
			break
		}
	}

	if !m.IsMethod() {
		base.Errorf("%v.%v is a field, not a method", t, s)
		return nil, followptr
	}

	return m, followptr
}

func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool {
	t0 := t
	if t == nil {
		return false
	}

	if t.IsInterface() {
		i := 0
		tms := t.AllMethods().Slice()
		for _, im := range iface.AllMethods().Slice() {
			for i < len(tms) && tms[i].Sym != im.Sym {
				i++
			}
			if i == len(tms) {
				*m = im
				*samename = nil
				*ptr = 0
				return false
			}
			tm := tms[i]
			if !types.Identical(tm.Type, im.Type) {
				*m = im
				*samename = tm
				*ptr = 0
				return false
			}
		}

		return true
	}

	t = types.ReceiverBaseType(t)
	var tms []*types.Field
	if t != nil {
		CalcMethods(t)
		tms = t.AllMethods().Slice()
	}
	i := 0
	for _, im := range iface.AllMethods().Slice() {
		if im.Broke() {
			continue
		}
		for i < len(tms) && tms[i].Sym != im.Sym {
			i++
		}
		if i == len(tms) {
			*m = im
			*samename, _ = ifacelookdot(im.Sym, t, true)
			*ptr = 0
			return false
		}
		tm := tms[i]
		if tm.Nointerface() || !types.Identical(tm.Type, im.Type) {
			*m = im
			*samename = tm
			*ptr = 0
			return false
		}
		followptr := tm.Embedded == 2

		// 如果指针接收器在方法中，
		// 对于值类型，该方法不存在。
		rcvr := tm.Type.Recv().Type
		if rcvr.IsPtr() && !t0.IsPtr() && !followptr && !types.IsInterfaceMethod(tm.Type) {
			if false && base.Flag.LowerR != 0 {
				base.Errorf("interface pointer mismatch")
			}

			*m = im
			*samename = nil
			*ptr = 1
			return false
		}
	}

	return true
}

func isptrto(t *types.Type, et types.Kind) bool {
	if t == nil {
		return false
	}
	if !t.IsPtr() {
		return false
	}
	t = t.Elem()
	if t == nil {
		return false
	}
	if t.Kind() != et {
		return false
	}
	return true
}

// lookdot0返回名为s的字段或方法的数量
// 用t型。如果正好存在一个，它将在*保存中返回
// （如果保存不是零）。
func lookdot0(s *types.Sym, t *types.Type, save **types.Field, ignorecase bool) int {
	u := t
	if u.IsPtr() {
		u = u.Elem()
	}

	c := 0
	if u.IsStruct() || u.IsInterface() {
		var fields *types.Fields
		if u.IsStruct() {
			fields = u.Fields()
		} else {
			fields = u.AllMethods()
		}
		for _, f := range fields.Slice() {
			if f.Sym == s || (ignorecase && f.IsMethod() && strings.EqualFold(f.Sym.Name, s.Name)) {
				if save != nil {
					*save = f
				}
				c++
			}
		}
	}

	u = t
	if t.Sym() != nil && t.IsPtr() && !t.Elem().IsPtr() {
		// 如果t是定义的指针类型，那么x.m是（*x）.m的缩写。
		u = t.Elem()
	}
	u = types.ReceiverBaseType(u)
	if u != nil {
		for _, f := range u.Methods().Slice() {
			if f.Embedded == 0 && (f.Sym == s || (ignorecase && strings.EqualFold(f.Sym.Name, s.Name))) {
				if save != nil {
					*save = f
				}
				c++
			}
		}
	}

	return c
}

var slist []symlink

// 用于帮助为嵌入式系统上的方法生成蹦床函数的代码
// 类型。这些与相应的AddImplicitDots大致相同
// 例程，但它们希望使用唯一的任务和
// 它们返回实际的方法。

type symlink struct {
	field *types.Field
}
